home *** CD-ROM | disk | FTP | other *** search
- /*
- File: FractalEngine.c
-
- Used to build: “Fractal 6”
-
- Written by: Jim Cathey July 1985
- Eric Traut November 1994
-
- Description:
- The following code implements a “Fractal Contour” generating
- program. See comments in the file “FractalMain.c” for more
- information.
-
- The code in this file implements the fractal-generating
- and plotting portion of the program.
- */
-
-
- #include <Quickdraw.h>
- #include <ToolUtils.h>
-
- #include <stdlib.h>
-
- #include "Fractal.h"
-
- /* Functions defined within this file */
- static double RandomDouble(void);
- static void CalcXs(long len, long incr, long sk);
- static void CalcYs(long len, long incr, long sk);
- static void CalcDiagonals(long len, long incr, long sk);
- static long GetZValue(long x, long y);
- static void SetZValue(long d, long x, long y);
- static void SeaLevel(long *newx, long *newy, long *newz);
- static void DoPlot(long xindex, long yindex);
- static void PlotTo(long x, long y, long z);
- static void DrawLine(long x, long y);
- static void Cordic(long *x, long *y, long scale, long count);
- static void ScaledMoveTo(long x, long y);
- static void ScaledLineTo(long x, long y);
- static void ScaledPoint(long x, long y);
- static void SetLineColor(unsigned short colorCode);
- static void LineToOffscreen(long x, long y);
- static void LineXOffscreen(long startX, long startY, long endX, long endY);
- static void LineYOffscreen(long startX, long startY, long endX, long endY);
- static void PointOffscreen(long x, long y);
-
- /* Global variables */
- long gMaxX, gMaxY;
- long gLevel; /* Current level */
- long gCurrentX, gCurrentY;
- unsigned char gPixelColor; /* Pixel value for offscreen line drawing */
- Boolean gAtLineStart; /* True at the first of the line */
- Boolean gOnLand; /* True when plotting land, else false */
- char* gPixelBase; /* Base of pixel store */
- long gRowBytesIncrement; /* Amount to increment each offscreen row */
- long gWindowScaleX; /* Amount to scale points in X direction */
- long gWindowScaleY; /* Amount to scale points in Y direction */
-
- /* Macros */
- #define Rotate(x, y) Cordic((x), (y), 3, 4)
- #define TiltDown(x, z) Cordic((x), (z), 3, (gContourType == kStyleMountains) ? 5 : -5)
- #define AbsoluteValue(v) ((v) > 0 ? (v) : -(v))
-
- /*
- RandomDouble
-
- This function returns a random number between -1 and 1.
- */
- double RandomDouble(void)
- {
- return (double)rand() / RAND_MAX;
- }
-
-
- /*
- CalcSurface
-
- This is the main surface-generating routine. It generates a random
- fractal surface by filling in the gPointArray array.
- */
- void CalcSurface(unsigned short level)
- {
- long i, j, length, incrby, sk;
- float power;
- long startTicks;
-
- gLevel = level;
-
- gTotalFractals++;
- gFractalChanged = true;
- startTicks = TickCount();
-
- gMaxX = 1 << level;
- gMaxY = gMaxX / 2;
- for (i = 0; i <= gMaxX; i++) /* Clear the Array. Use i & incrby as temps */
- for (incrby = 0; incrby <= gMaxY; incrby++)
- (*gPointArray)[i][incrby] = 0;
-
- for (i = 1; i <= level; i++) {
- for (power = 1.0, j = 0; j < i; j++)
- power *= 1.8;
- length = 10000 / power; /* = 10000/(1.8^i) */
- incrby = gMaxX / (1 << i); /* # of line segments in a side of the triangle */
- sk = incrby * 2;
- CalcXs(length, incrby, sk);
- CalcYs(length, incrby, sk);
- CalcDiagonals(length, incrby, sk);
- }
-
- gTotalTickCount += TickCount() - startTicks;
- }
-
-
- /*
- CalcXs
-
- This function calculates the fractal values in the X direction.
- It is called for each level and subdivides the existing edges
- of the fractal to get the next level.
- */
- void CalcXs(long len, long incr, long sk)
- {
- long y, x;
- long d1, d2;
-
- for (y=0; y < gMaxX; y += sk) {
- for (x = incr+y; x <= gMaxX; x += sk) {
- d1 = GetZValue(x-incr, y);
- d2 = GetZValue(x+incr, y);
- SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
- }
- }
- }
-
-
- /*
- CalcYs
-
- This function calculates the fractal values in the Y direction.
- It is called for each level and subdivides the existing edges
- of the fractal to get the next level.
- */
- void CalcYs(long len, long incr, long sk)
- {
- long y, x;
- long d1, d2;
-
- for (x = gMaxX; x >= 1; x -= sk)
- for (y = incr; y <= x; y += sk) {
- d1 = GetZValue(x, y + incr);
- d2 = GetZValue(x, y - incr);
- SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
- }
- }
-
-
- /*
- CalcDiagonals
-
- This function calculates the fractal values in the diagonal direction.
- It is called for each level and subdivides the existing edges
- of the fractal to get the next level.
- */
- void CalcDiagonals(long len, long incr, long sk)
- {
- long y, x;
- long d1, d2;
-
- for (x = 0; x < gMaxX; x += sk)
- for (y = incr; y <= gMaxX - x; y += sk) {
- d1 = GetZValue(x + y - incr, y - incr);
- d2 = GetZValue(x + y + incr, y + incr);
- SetZValue(((d1 + d2) >> 1) + (long)(RandomDouble() * (len >> 1)) - (len >> 2), x + y, y);
- }
- }
-
-
- /*
- GetZValue
-
- This function returns the Z value (i.e. the value stored in the
- two-dimensional gPlotArray array. Because the fractal is triangular
- and the array is square, we will store half the triange in the lower
- part of the array the the other half in the upper part of the
- array.
- */
- long GetZValue(long x, long y)
- {
- if (y <= gMaxY)
- return (*gPointArray)[x][y];
- else
- return (*gPointArray)[gMaxX - x][gMaxX + 1 - y];
- }
-
-
- /*
- SetZValue
-
- This function saves the Z value (i.e. the value in the
- two-dimensional gPlotArray array. Because the fractal is triangular
- and the array is square, we will store half the triange in the lower
- part of the array the the other half in the upper part of the
- array.
- */
- void SetZValue(long d, long x, long y)
- {
- if (y <= gMaxY)
- (*gPointArray)[x][y] = d;
- else
- (*gPointArray)[gMaxX - x][gMaxX + 1 - y] = d;
- }
-
-
- /*
- PlotData
-
- Our window is already open at this point, and it is cleared.
- All we have to do now is fill it. This function draws the
- 2D projection of the triangular database on the screen.
- */
- void PlotData(void)
- {
- long xindex, yindex;
-
- gPixelBase = (char*)GetPixBaseAddr(gOffscreenPixMap);
- gRowBytesIncrement = (*gOffscreenPixMap)->rowBytes & 0x7FFF;
-
- gWindowScaleX = (kNewScreenX << kScaleShift) / kOriginalScreenX;
- gWindowScaleY = (kNewScreenY << kScaleShift) / kOriginalScreenY;
-
- gOnLand = true; /* On land to start. */
-
- for (xindex = 0; xindex <= gMaxX; xindex++) { /* Plot along X axis. */
- gAtLineStart = true;
- for (yindex = 0; yindex <= xindex; yindex++)
- DoPlot(xindex, yindex);
- }
-
- for (yindex = 0; yindex <= gMaxX; yindex++) { /* Plot along Y axis. */
- gAtLineStart = true;
- for (xindex = yindex; xindex <= gMaxX; xindex++)
- DoPlot(xindex, yindex);
- }
-
- for (xindex = 0; xindex <= gMaxX; xindex++) { /* Plot along the diagonal. */
- gAtLineStart = true;
- for (yindex=0; yindex <= gMaxX - xindex; yindex++)
- DoPlot(xindex + yindex, yindex);
- }
- }
-
-
- /*
- DoPlot
-
- This function plots a single line or point from the current X and
- Y value to the new X and Y value specified.
- */
- void DoPlot(long xindex, long yindex)
- {
- long xcoord, ycoord, zcoord;
-
- zcoord = GetZValue(xindex, yindex);
- ycoord = (yindex * 10000) >> gLevel;
- xcoord = ((xindex * 10000) >> gLevel) - ycoord/2;
- if (gContourType == kStyleWater)
- SeaLevel(&xcoord, &ycoord, &zcoord);
- PlotTo(xcoord, ycoord, zcoord);
- }
-
-
- /*
- SeaLevel
-
- This function is used to plot lines when using the “foothills
- with water” mode. It determines whether a line goes under
- water at any point. If it does, it simply draws a point
- to represent the water.
- */
- void SeaLevel(long *newX, long *newY, long *newZ)
- {
- static long oldX, oldY, oldZ; /* The starting point for the next call */
- long waterX, waterY, waterZ; /* Where the vector hits the waterline. */
- float scratch;
-
- if (gAtLineStart) { /* If at the beginning of the line */
- if ((oldZ = *newZ) < 0) { /* and if we’re underwater */
- SetLineColor(blueColor);
- gOnLand = false;
- *newZ = 0; /* Clip to the waterline */
- }
- else {
- SetLineColor(blackColor);
- gOnLand = true; /* Otherwise we’re on land from the start */
- }
- }
- else { /* Else we’re in the middle of a line and */
- if (oldZ > 0 && *newZ > 0) { /* Start & end points both above water.. */
- oldZ = *newZ;
- }
- else if (oldZ < 0 && *newZ < 0) { /* Start & end points both under water... */
- oldZ = *newZ;
- *newZ = 0; /* Clip at the waterline */
- }
- else { /* We’re now crossing the waterline, so calculate */
- /* the exact point where it dives under */
- if (*newZ == oldZ) {
- waterX = oldX;
- waterY = oldY;
- }
- else {
- scratch = (float) *newZ / (*newZ - oldZ); /* Proportion of the line that’s */
- waterX = (long) ((oldX - *newX) * scratch) + *newX; /* below the water */
- waterY = (long) ((oldY - *newY) * scratch) + *newY;
- }
- waterZ = 0;
-
- PlotTo(waterX, waterY, waterZ); /* Draw to the waterline first */
-
- /* The plot from the waterline to the endpoint in the new color is done elsewhere */
- if (*newZ > 0) { /* Emerging from the water */
- SetLineColor(blackColor);
- gOnLand = true;
- oldZ = *newZ;
- }
- else { /* Diving into the water. */
- SetLineColor(blueColor);
- gOnLand = false;
- oldZ = *newZ;
- *newZ = 0;
- }
- }
- }
- oldX = *newX; /* Save the real endpoint of the vector */
- oldY = *newY; /* to use as the start of the next call */
- }
-
-
- /*
- PlotTo
-
- This function converts a 3-D line to a 2-D line and plots it
- */
- void PlotTo(long x, long y, long z)
- {
- long tempNum;
-
- Rotate(&x, &y); /* Rotate 30 deg. towards Y in the XY plane */
- TiltDown(&x, &z); /* Tip 36 deg. down in the ZX plane */
-
- tempNum = y; /* Compiler generates better code with temp register */
- y = (tempNum >> 5) + (tempNum >> 7); /* Scale 10K to 400. */
- tempNum = z;
- z = (tempNum >> 5) + (tempNum >> 7);
-
- DrawLine(y, z); /* Show the YZ planar projection. */
- }
-
-
- /*
- DrawLine
-
- This function draws a line from the last endpoint
- */
- void DrawLine(long x, long y)
- {
- x += (x >> 4) + (x >> 5);
- x += 10; /* Compute x 1.09 + 10 */
- if (gContourType == kStyleMountains)
- y = 220 - y; /* Move the baseline for mountains */
- else
- y = 80 - y;
-
- if (!gOnLand) {
- ScaledPoint(x, y);
- }
- else {
- if (gAtLineStart) {
- ScaledMoveTo(x, y);
- gAtLineStart = false;
- }
- ScaledLineTo(x, y);
- }
- }
-
-
- /*
- Cordic
-
- This function spins the XY vector ‘count’ steps to the left
- using a CORDIC algorithm with a shift factor of ‘scale.’
- Rotates atan(1/(2^scale)) degrees/step (e.g. scale of 5 is
- 1.79 deg/step; 4 = 3.57 d/s...)
-
- *x and *y should be large for accuracy.
- */
- void Cordic(long *x, long *y, long scale, long count)
- {
- long tempX, tempY;
-
- tempX = *x;
- tempY = *y;
- if (count > 0) /* Positive count is CCW (left) */
- for (; count; count--) {
- tempX -= (tempY >> scale);
- tempY += (tempX >> scale);
- }
- else /* Negative is CW (right) */
- for (; count; count++) {
- tempX += (tempY >> scale);
- tempY -= (tempX >> scale);
- }
- *x = tempX;
- *y = tempY;
- }
-
-
- /*
- ScaledMoveTo
-
- This function performs a scaled MoveTo by scaling the given
- point from MacPlus screen coordinates to the size of the
- current main window.
- */
- void ScaledMoveTo(long x, long y)
- {
- gCurrentX = (x * gWindowScaleX) >> kScaleShift;
- gCurrentY = (y * gWindowScaleY) >> kScaleShift;
- }
-
-
- /*
- ScaledLineTo
-
- This function performs a scaled LineTo by scaling the given
- point from MacPlus screen coordinates to the size of the
- current main window.
- */
- void ScaledLineTo(long x, long y)
- {
- LineToOffscreen((x * gWindowScaleX) >> kScaleShift,
- (y * gWindowScaleY) >> kScaleShift);
- }
-
-
- /*
- ScaledPoint
-
- This function draws a scaled pixel by scaling the given
- point from MacPlus screen coordinates to the size of the
- current main window.
- */
- void ScaledPoint(long x, long y)
- {
- PointOffscreen((x * gWindowScaleX) >> kScaleShift,
- (y * gWindowScaleY) >> kScaleShift);
- }
-
-
- /*
- SetLineColor
-
- This function sets the value of the global gPixelColor
- to the appropriate value for the give color.
- */
- void SetLineColor(unsigned short colorCode)
- {
- RGBColor curColor;
-
- ForeColor(colorCode);
- GetForeColor(&curColor);
-
- gPixelColor = Color2Index(&curColor);
- }
-
-
- /*
- LineToOffscreen
-
- This function replaces the LineTo function with a custom
- off-screen line drawing function.
- */
- void LineToOffscreen(long x, long y)
- {
- if (AbsoluteValue(y - gCurrentY) > AbsoluteValue(x - gCurrentX)) {
- if (y < gCurrentY)
- LineYOffscreen(x, y, gCurrentX, gCurrentY);
- else
- LineYOffscreen(gCurrentX, gCurrentY, x, y);
- }
- else {
- if (x < gCurrentX)
- LineXOffscreen(x, y, gCurrentX, gCurrentY);
- else
- LineXOffscreen(gCurrentX, gCurrentY, x, y);
- }
-
- gCurrentX = x;
- gCurrentY = y;
- }
-
-
- /*
- LineXOffscreen
-
- Draws lines with a slope between -1 and 1
- */
- void LineXOffscreen(long startX, long startY, long endX, long endY)
- {
- long currentX;
- float slope, currentY;
- char* pixelAddress;
- long deltaY;
- float newY;
-
- if (endX == startX)
- slope = 0;
- else
- slope = (float)(endY - startY) / (float)(endX - startX);
-
- pixelAddress = gPixelBase + startX;
- pixelAddress += (unsigned long)(startY * gRowBytesIncrement);
- currentY = startY + (float)0.5;
-
- for (currentX = startX; currentX <= endX; currentX++) {
- *pixelAddress++ = gPixelColor;
- newY = currentY + slope;
- deltaY = (long)newY - (long)currentY;
- if (deltaY)
- pixelAddress += (long)(gRowBytesIncrement * deltaY);
- currentY = newY;
- }
- }
-
-
- /*
- LineYOffscreen
-
- Draws lines with a slope <= -1 or >= 1
- */
- void LineYOffscreen(long startX, long startY, long endX, long endY)
- {
- long currentY;
- float slope, currentX, newX;
- char* pixelAddress;
-
- if (endY == startY)
- slope = 0;
- else
- slope = (float)(endX - startX) / (float)(endY - startY);
-
- pixelAddress = gPixelBase + startX;
- pixelAddress += (unsigned long)(startY * gRowBytesIncrement);
- currentX = startX + 0.5;
-
- for (currentY = startY; currentY <= endY; currentY++) {
- *pixelAddress = gPixelColor;
- newX = currentX + slope;
- pixelAddress += gRowBytesIncrement + (long)(newX) - (long)(currentX);
- currentX = newX;
- }
- }
-
-
- /*
- PointOffscreen
-
- Draws a single pixel
- */
- void PointOffscreen(long x, long y)
- {
- char* pixelAddress;
-
- pixelAddress = gPixelBase + x;
- pixelAddress += (unsigned long)(y * gRowBytesIncrement);
- *pixelAddress = gPixelColor;
-
- gCurrentX = x;
- gCurrentY = y;
- }
-
-
-
-
-
-
-
-